package no.ntnu.fp.gui;

import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.awt.event.WindowEvent;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;

import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.ListSelectionModel;
import javax.swing.ToolTipManager;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableCellRenderer;

import no.ntnu.fp.client.Appointment;
import no.ntnu.fp.client.CalendarClient;
import no.ntnu.fp.client.User;

public class WeeklyCalendarGui {
	private JLabel lblWeek, lblWeekBox, lblUser, lblCompareToUserBox;
	private JButton btnPrev, btnNext;
	private JTable tblCalendar;
	private JComboBox cmbWeekBox, cmbCompareToUserBox;
	private JFrame frmMain;
	private Container pane;
	private DefaultTableModel mtblCalendar; //Table model
	private JScrollPane stblCalendar; //The scrollpane
	private JPanel pnlCalendar; //The panel
	private int realWeek, currentWeek;
	private String realDate;
	private CalendarClient calendarClient;
	private Calendar cal = Calendar.getInstance();
	private String[] datesInWeek = new String[7];
	private DateFormat dbDate = new SimpleDateFormat("d:M:yyyy");
	private ArrayList<User> userList;
	private User loggedInAs, compareTo;
	

	public DefaultTableModel getCalendarModel() {
		return mtblCalendar;
	}
	
	public WeeklyCalendarGui(CalendarClient cc) {
		// Set CalendarClient
		calendarClient = cc;
		
		// Set tooltips
		ToolTipManager ttm;
		ttm = ToolTipManager.sharedInstance();
		ttm.setEnabled(true);
		ttm.setInitialDelay(500);
		ttm.setDismissDelay(3000);

		//Prepare frame
		frmMain = new JFrame ("Calendar"); //Create frame
		frmMain.setSize(1024, 600); //Set size
		pane = frmMain.getContentPane(); //Get content pane
		pane.setLayout(null); //Apply null layout
		frmMain.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); //Close when X is clicked
		frmMain.addWindowListener(new java.awt.event.WindowAdapter() {
			public void windowClosing(WindowEvent winEvt) {
				calendarClient.disconnectFromServer();
				System.exit(0);
			}
		});

		//Create controls
		lblWeek = new JLabel ("Week 1");
		lblWeekBox = new JLabel ("Select week: ");
		cmbWeekBox = new JComboBox();
		lblUser = new JLabel("User: " + calendarClient.getLoggedInUser().getName());
		lblCompareToUserBox = new JLabel("Compare to user: ");
		cmbCompareToUserBox = new JComboBox();
		btnPrev = new JButton ("<<");
		btnNext = new JButton (">>");
		mtblCalendar = new DefaultTableModel();
		tblCalendar = new JTable(mtblCalendar) {
			private static final long serialVersionUID = 1L;
			public boolean isCellEditable(int rowIndex, int columnIndex) {
				return false;
			}
		};
		stblCalendar = new JScrollPane(tblCalendar);
		pnlCalendar = new JPanel(null);
		
		//Set border
		pnlCalendar.setBorder(BorderFactory.createTitledBorder("Calendar")); 
		
		//Add controls to pane
		pane.add(pnlCalendar);
		pnlCalendar.add(lblWeek);
		pnlCalendar.add(lblWeekBox);
		pnlCalendar.add(cmbWeekBox);
		pnlCalendar.add(lblUser);
		pnlCalendar.add(lblCompareToUserBox);
		pnlCalendar.add(cmbCompareToUserBox);
		pnlCalendar.add(btnPrev);
		pnlCalendar.add(btnNext);
		pnlCalendar.add(stblCalendar);

		//Set bounds
		pnlCalendar.setBounds(0, 0, 1014, 565);
		lblWeek.setBounds(480-lblWeek.getPreferredSize().width/2, 20, 600, 25);
		lblUser.setBounds(10, 20, 150, 20);
		lblWeekBox.setBounds(150, 20, 80, 20);
		cmbWeekBox.setBounds(230, 21, 50, 20);
		lblCompareToUserBox.setBounds(10, 530, 200, 20);
		cmbCompareToUserBox.setBounds(230, 530, 100, 20);
		btnPrev.setBounds(360-lblWeek.getPreferredSize().width/2, 20, 50, 25);
		btnNext.setBounds(600-lblWeek.getPreferredSize().width/2, 20, 50, 25);
		stblCalendar.setBounds(10, 50, 990, 475);
		
		// Set main window settings
		frmMain.setResizable(false);
		frmMain.setVisible(true);
		
		// Get calendar set to current date and time
		cal.setFirstDayOfWeek(Calendar.MONDAY);
		realWeek = currentWeek = cal.get(Calendar.WEEK_OF_YEAR);
		realDate = dbDate.format(cal.getTime());
		
		//Populate combo box with weeks
		for (int i = 0; i <= 51; i++){
			cmbWeekBox.addItem(String.valueOf(i+1));
		}

		//Populate combo box with users
		userList = calendarClient.getUsers();
		loggedInAs = calendarClient.getLoggedInUser();
		int userIndex = 0;
		for (int i = 0; i < userList.size(); i++) {
			if (userList.get(i).getName().equals(loggedInAs.getName())) {
				userIndex = i;
			}
			cmbCompareToUserBox.addItem(userList.get(i).getName());
		}
		cmbCompareToUserBox.setSelectedIndex(userIndex);
		
		//Add headers
		for (int i = 0; i < 7; i++){
			mtblCalendar.addColumn("Date");
		}
		
		//Set background
		tblCalendar.getParent().setBackground(tblCalendar.getBackground()); 
		
		//No resize/reorder
		tblCalendar.getTableHeader().setResizingAllowed(false);
		tblCalendar.getTableHeader().setReorderingAllowed(false);
		
		//Single cell selection
		tblCalendar.setColumnSelectionAllowed(true);
		tblCalendar.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
		//Mouse listeners
		tblCalendar.addMouseListener(new java.awt.event.MouseAdapter() {
			public void mouseClicked(java.awt.event.MouseEvent evt) {
				tblCalendarMouseClicked(evt);
			}
		});
		tblCalendar.addMouseMotionListener(new MouseMotionAdapter() {
			public void mouseMoved(MouseEvent e){
				Point p = e.getPoint();
				int row = tblCalendar.rowAtPoint(p);
				int column = tblCalendar.columnAtPoint(p);
				if (row == 0) {
					String[] date = datesInWeek[column].split(":");
					int day = Integer.valueOf(date[0]);
					int month = Integer.valueOf(date[1]);
					int year = Integer.valueOf(date[2]);
					ArrayList<Appointment> list = calendarClient
							.getDatesAppointments(day, month, year);
					Appointment appointment = null;
					for (Appointment a : list) {
						appointment = a;
					}
					try {
						String meeting = appointment.toFullString();
						tblCalendar.setToolTipText(meeting);
					} catch (Exception ex) {
						tblCalendar.setToolTipText(null);
					}
				}
			}
		});
		
		//Set row/column count
		tblCalendar.setRowHeight(223);
		mtblCalendar.setColumnCount(7);
		mtblCalendar.setRowCount(2);
		
		//Register action listeners
		btnPrev.addActionListener(new btnPrev_Action());
		btnNext.addActionListener(new btnNext_Action());
		cmbWeekBox.addActionListener(new cmbWeekBox_Action());
		cmbCompareToUserBox.addActionListener(new cmbCompareToUserBox_Action());
		
		refreshCalendar();
		
		//Answer any new notifications since last login
		calendarClient.displayNotifications();
	}
	
	public void refreshCalendar() {
		// Debug
		System.out.println("realWeek: " + String.valueOf(realWeek));
		System.out.println("currentWeek: " + String.valueOf(currentWeek));
		
		// Enable buttons at first
		btnPrev.setEnabled(true);
		btnNext.setEnabled(true);
		
		// Cap for one year
		if (currentWeek == 1) { btnPrev.setEnabled(false); }
		if (currentWeek == 52) { btnNext.setEnabled(false); }
		
		// Update labels and combobox
		lblWeek.setText("Week " + String.valueOf(currentWeek));
		cmbWeekBox.setSelectedIndex(currentWeek-1);
		if (compareTo != null) {
			lblCompareToUserBox.setText("Compare to: " + compareTo.getName());
		}
		
		//Clear table and column headers
		for (int i=0; i<7; i++){
			mtblCalendar.setValueAt(null, 0, i);
			mtblCalendar.setValueAt(null, 1, i);
		}
		
		// Make a copy of calendar so we dont mess up our dates
		Calendar tmpCal = (Calendar) cal.clone();
		// Create new SimpleDate for date format
		DateFormat columnDate = new SimpleDateFormat("EEEE   dd/MM");
		// Set the calendar to monday of the current week
		tmpCal.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);
		for (int i = 0; i < 7; i++) {
			datesInWeek[i] = dbDate.format(tmpCal.getTime());
			tblCalendar.getColumnModel().getColumn(i).setHeaderValue(columnDate.format(tmpCal.getTime())); // Set new date in column header
			tblCalendar.getTableHeader().repaint(); // Repaint column header
			tmpCal.add(Calendar.DATE, 1); // Next day for week display
		}
		
		// Draw users appointments
		for (int i = 0; i < 7; i++) {
			String[] date = datesInWeek[i].split(":");
			ArrayList<Appointment> appointments = calendarClient.getDatesAppointments(Integer.valueOf(date[0]), Integer.valueOf(date[1]), Integer.valueOf(date[2]));
			for (Appointment app : appointments) {
				mtblCalendar.setValueAt(app, 0, i);
			}
		}

		// If compare to user is set
		if (compareTo != null) {
			ArrayList<Appointment> appointments = calendarClient.getAllUserAppointments(compareTo.getUname());
			// Draw compare to users appointments
			for (int i = 0; i < 7; i++) {
				String[] date = datesInWeek[i].split(":");
				for (Appointment app : appointments) {
					if (app.getDay() == Integer.valueOf(date[0]) && app.getMonth() == Integer.valueOf(date[1]) && app.getYear() == Integer.valueOf(date[2])) {
						mtblCalendar.setValueAt(app, 1, i);
					}
				}
			}
		}

		// Apply render
		tblCalendar.setDefaultRenderer(tblCalendar.getColumnClass(0), (TableCellRenderer) new tblCalendarRenderer());
	}
	
	private class tblCalendarRenderer extends DefaultTableCellRenderer{
		private static final long serialVersionUID = 4163815180219777903L;

		public Component getTableCellRendererComponent (JTable table, Object value, boolean selected, boolean focused, int row, int column){
			super.getTableCellRendererComponent(table, value, selected, focused, row, column);
			// Default gray background
			setBackground(new Color(255, 255, 255));
			// Blue day if current
			if (realDate.equals(datesInWeek[column]))
				setBackground(new Color(220, 220, 255));
			// Green day if selected
			if (selected == true)
                setBackground(new Color(152,251,152));
			// Defaults
			setBorder(null);
			setForeground(Color.black);
			return this;  
		}
	}
	
	private class btnPrev_Action implements ActionListener {
		@Override
		public void actionPerformed(ActionEvent e) {
			if (currentWeek != 1) {
				cal.roll(Calendar.WEEK_OF_YEAR, -1);
				currentWeek = cal.get(Calendar.WEEK_OF_YEAR);
				refreshCalendar();
			}
		}
	}
	
	private class btnNext_Action implements ActionListener {
		@Override
		public void actionPerformed(ActionEvent e) {
			if (currentWeek != 52) {
				cal.roll(Calendar.WEEK_OF_YEAR, 1);
				currentWeek = cal.get(Calendar.WEEK_OF_YEAR);
				refreshCalendar();
			}
		}
	}
	
	private class cmbWeekBox_Action implements ActionListener {
		@Override
		public void actionPerformed(ActionEvent e) {
			if (Integer.parseInt(cmbWeekBox.getSelectedItem().toString()) != currentWeek) {
				int week = Integer.parseInt(cmbWeekBox.getSelectedItem().toString());
				cal.roll(Calendar.WEEK_OF_YEAR, week-currentWeek);
				currentWeek = cal.get(Calendar.WEEK_OF_YEAR);
				refreshCalendar();
			}
		}
	}
	
	private class cmbCompareToUserBox_Action implements ActionListener {
		@Override
		public void actionPerformed(ActionEvent e) {
			String user = cmbCompareToUserBox.getSelectedItem().toString();
			if (!user.equals(loggedInAs.getName())) {
				for (User a : userList) {
					if (a.getName().equals(user)) {
						compareTo = a;
						refreshCalendar();
						break;
					}
				}
			}
		}
	}
	
	private void tblCalendarMouseClicked(MouseEvent evt) {
		try {
			int row = tblCalendar.getSelectedRow();
			int column = tblCalendar.getSelectedColumn();
			if (row == 0) {
				String[] date = datesInWeek[column].split(":");
				int day = Integer.valueOf(date[0]); int month = Integer.valueOf(date[1]); int year = Integer.valueOf(date[2]);
				ArrayList<Appointment> appointments = calendarClient.getDatesAppointments(day, month, year);
				NewAppointmentDialog dialog = new NewAppointmentDialog(calendarClient, row, column, day, month, year, appointments);
				dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
				dialog.setVisible(true);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public void refresh() {
		refreshCalendar();
		
	}
}